#include "abc-static-resource-controller.h"

#include <fstream>

#include "util/fs.h"
#include "util/strutil.h"
#include "util/fs-util.h"
#include "util/compress.h"
#include "util/logger/logger.h"
#include "util/config/preference-entry.h"
#include "util/config/ui-path.h"
#include "util/net/http-request.h"
#include "util/net/http-response.h"


static const char *title() {
    return "AbcStaticResourceController ";
}

static const std::string kStaticResourceUriPrefix("/static/");

static const PreferenceEntry<UiPathVec> prefResourceDirs("resource.directories");

static std::optional<fs::path> findResource(const std::string &relative) {
    for (const UiPath &uiPath : *prefResourceDirs) {
        fs::path p(uiPath.str());
        p /= relative;
        if (fs::exists(p) && !fs::is_directory(p)) {
            return p.lexically_normal();
        }
    }
    return std::nullopt;
}

AbcStaticResourceController::AbcStaticResourceController() {
}

void AbcStaticResourceController::handleHttpRequest(const HttpRequest &request, HttpResponse &response) {
    if (!strutil::startWith(request.uri(), kStaticResourceUriPrefix)) {
        response.setStatusCode(HttpStatusCode::NotFound);
        return;
    }

    const std::string relative(request.uri().substr(kStaticResourceUriPrefix.length()));
    LOGD << title() << "static resource request: [" << relative << "]";

    const std::optional<fs::path> pfile = findResource(relative);
    if (!pfile) {
        response.setStatusCode(HttpStatusCode::NotFound);
        return;
    }

    const fs::path file = *pfile;
    LOGD << title() << "static resource file path: [" << file << "]";

    const fs::file_time_type lastWriteTime = fs::last_write_time(file);
    const std::time_t seconds = std::chrono::duration_cast<std::chrono::seconds>(lastWriteTime.time_since_epoch()).count();
    const std::string etag = strutil::convert(seconds);
    response.setEtag(etag);
    response.setContentType(http::ContentType::fromExt(strutil::ext(relative)));

    if (etag == request.ifNonMatch()) {
        response.setStatusCode(HttpStatusCode::NotModified);
        return;
    }

    response.buffer() = fsutil::content(file);

    if (request.acceptGzip()) {
        compress::gzip(response.buffer());
        response.markGzipped();
    }
}
